package com.comm.tools;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import com.game.majiang.config.LogManager;
import org.apache.commons.lang3.StringUtils;

public class Tools {
    final static String reqFile = "template" + File.separator + "req.template";
    final static String rspFile = "template" + File.separator + "rsp.template";
    final static String switchFile = "template" + File.separator + "switch.template";
    final static String executeFile = "template" + File.separator + "execute.template";

    static String srcPath;
    static String cntPath;
    static String serPath;

    static StringBuffer hBuf = new StringBuffer();
    static StringBuffer cppBuf = new StringBuffer();

    static StringBuffer msgHBuf = new StringBuffer();
    static StringBuffer msgCppBuf = new StringBuffer();

    static final int TYPE_SERVER = 1;
    static final int TYPE_CLIENT = 2;
    static final int KEY_STRUCT = 0;
    static final int KEY_NOTE = 1;
    static final int KEY_FIELD = 2;
    static final int KEY_END_STRUCT = 3;
    static int cmdIndex = 0;

    static int MAX_PATTERN_COUNT = 100;

    static int patCount = 0;
    static Pattern[] pats = new Pattern[MAX_PATTERN_COUNT];
    static int[] patType = new int[MAX_PATTERN_COUNT];

    public static void init(String srcPath, String cntPath, String serPath) throws IOException {
        Tools.srcPath = srcPath;
        Tools.cntPath = cntPath;
        Tools.serPath = serPath;
        InitPer();

        ArrayList<File> fileList = FileService.getFiles(srcPath, "*.h");

        if (fileList == null) {
            LogManager.getInstance().Log("Can't find any files in '" + srcPath + "'.");
            return;
        }

        for (int i = 0; i < fileList.size(); i++) {
            hBuf = new StringBuffer();
            cppBuf = new StringBuffer();

            msgHBuf = new StringBuffer();
            msgCppBuf = new StringBuffer();

            // erMsgBuf = new StringBuffer();
            // erMsgFunBuf = new StringBuffer();

            String fileKey = ((File) fileList.get(i)).getName();
            fileKey = fileKey.substring(0, fileKey.length() - 2);

            hBuf.append("\n#pragma  once \n\n");

            hBuf.append("#include <vector> \n\n");
            hBuf.append("#include \"NetDef.h\" \n\n");
            hBuf.append("using namespace std;\n\n");

            hBuf.append("namespace pk{\n\n");

            msgHBuf.append("\n#pragma  once \n\n");

            msgHBuf.append("#include <vector> \n\n");
            msgHBuf.append("using namespace std;\n\n");

            msgCppBuf.append("#include \"NetDef.h\" \n\n");
            msgCppBuf.append("#include \"message.h\" \n\n");
            msgCppBuf.append("#include \"package.h\" \n\n");

            cppBuf.append("#include \"NetDef.h\" \n\n");
            cppBuf.append("#include \"package.h\" \n\n");
            cppBuf.append("#include \"message.h\" \n\n");

            cppBuf.append("\nnamespace pk{\n\n");

            msgCppBuf.append("void DoMessage_" + fileKey + "(char* data)\n");
            msgCppBuf.append("{\n");

            msgCppBuf.append("\tBEING_DEAL();\n");

            Parse((File) fileList.get(i));

            msgCppBuf.append("\tEND_DEAL();\n");
            msgCppBuf.append("}\n");

            hBuf.append("\n};\n\n");
            cppBuf.append("\n};\n\n");

            try {
                File f = new File(Tools.cntPath + File.separator + "pk_" + fileKey + ".h");
                f.delete();
                f = new File(Tools.cntPath + File.separator + "pk_" + fileKey + ".cpp");
                f.delete();

                OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(Tools.cntPath + File.separator + "pk_" + fileKey + ".h"));
                out.write(hBuf.toString());

                out.flush();
                out.close();

                out = new OutputStreamWriter(new FileOutputStream(Tools.cntPath + File.separator + "pk_" + fileKey + ".cpp"));
                out.write(cppBuf.toString());

                out.flush();
                out.close();

                out = new OutputStreamWriter(new FileOutputStream(Tools.cntPath + File.separator + "msg_" + fileKey + ".h"));
                out.write(msgHBuf.toString());

                out.flush();
                out.close();

                out = new OutputStreamWriter(new FileOutputStream(Tools.cntPath + File.separator + "msg_" + fileKey + ".cpp"));
                out.write(msgCppBuf.toString());

                out.flush();
                out.close();

                // out = new OutputStreamWriter(new FileOutputStream(erDesPath + "\\" + fileKey + ".erl"));

                // out.write(erMsgFunBuf.toString());

                // out.write(erMsgSendBuf.toString());
                // out.write(erMsgOnBuf.toString());

                // out.flush();
                // out.close();
            } catch (IOException localIOException) {
                LogManager.getInstance().Log(localIOException.getMessage());
            }
        }

        LogManager.getInstance().Log("end.");
    }

    static void Parse(File file) throws IOException {
        Pattern pkgPat = Pattern.compile("package\\s+([\\w.]+)\\s*;");
        cmdIndex = cmdIndex / 100 * 100;
        cmdIndex += 100;

        LogManager.getInstance().Log("Parse " + file.getAbsolutePath() + " ...");

        String structName = "";
        int type = 0;

        ArrayList<String> fileds = new ArrayList<String>();

        fileds.clear();

        BufferedReader reader = null;
        try {
            FileInputStream fileHanlde = new FileInputStream(file.getAbsolutePath());

            reader = new BufferedReader(new InputStreamReader(fileHanlde, "GBK"));
        } catch (IOException e) {
            return;
        }

        ArrayList<String> classname = new ArrayList<String>();
        boolean temp = false;//
        boolean bl = false;//
        while (true) {

            String line = "";
            try {
                line = reader.readLine();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (line == null) break;
            // LogManager.getInstance().Log(line);
            int perType = -1;
            Matcher matcher = null;

            for (int i = 0; i < patCount; i++) {
                matcher = pats[i].matcher(line);
                if (!matcher.find()) {
                    continue;
                }

                perType = patType[i];
                break;
            }

            switch (perType) {
                case 0:
                    fileds.clear();
                    type = 0;
                    structName = matcher.group(1);

                    if (matcher.group(2).equals("<")) type |= 1;
                    if (matcher.group(4).equals(">")) {
                        type |= 2;
                        classname.add(structName);
                    }
                    // �ж����޷���
                    if (!matcher.group(2).equals("<") && !matcher.group(4).equals(">")) {
                        temp = true;
                        type |= 1;
                        LogManager.getInstance().Log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                    } else {
                        temp = false;
                    }
                    break;
                case 1:
                    break;
                case 2:
                    fileds.add(matcher.group(1));
                    fileds.add(matcher.group(2));
                    fileds.add(matcher.group(4));
                    LogManager.getInstance().Log(fileds.toString());
                    break;
                case 3:
                    cmdIndex += 1;

                    if ((type & 0x2) != 0) {
                        String contents = OpenFile(Tools.reqFile);
                        Matcher pkgMat = pkgPat.matcher(contents);
                        String pkgStr = "";
                        if (pkgMat.find()) {
                            pkgStr = pkgMat.group(1);
                        }
                        contents = contents.replace("{0}", "Abstract" + structName);
                        contents = contents.replace("{1}", String.format("%d", cmdIndex));

                        StringBuilder sb1 = new StringBuilder();
                        StringBuilder sb2 = new StringBuilder();
                        StringBuilder sb3 = new StringBuilder();
                        int j = 0;
                        // ���req����
                        for (int i = 0; i < fileds.size(); i += 3) {
                            sb1.append("@Getter protected " + Tools.FieldTypeReplace(fileds.get(i), fileds.get(i + 1)) + " " + fileds.get(i + 2) + ";\n\r\t");
                            if (fileds.get(i).equals("vector")) {
                                String name = replace(fileds.get(i + 1));
                                String name1 = Tools.FieldTypeReplace(name);
                                if (name1.equals(name)) {
                                    sb3.append(funReadObj.replace("{0}", name1).replace("{1}", String.format("%d", ++j)));
                                } else {
                                    sb3.append(funReadBase.replace("{0}", name1).replace("{1}", String.format("%d", ++j)));
                                }
                                sb2.append(fileds.get(i + 2) + " = ReadArray" + String.format("%d", j) + "(buffer);\n\t");
                            } else
                                sb2.append(fileds.get(i + 2) + " = Read" + Tools.FieldTypeReplace(fileds.get(i), fileds.get(i + 1)) + "(buffer);\n\t");

                            sb2.append("\t");
                        }
                        contents = contents.replace("{2}", sb1.toString());
                        contents = contents.replace("{3}", sb2.toString());
                        contents = contents.replace("{4}", sb3.toString());

                        String path = Tools.serPath + File.separator + pkgStr.replace(".", File.separator);
                        new File(path).mkdirs();
                        String absJavaName = "Abstract" + structName;
                        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(path + File.separator + absJavaName + ".java"));
                        out.write(contents);
                        out.flush();
                        out.close();

                        contents = OpenFile(Tools.executeFile);
                        contents = contents.replace("{0}", pkgStr + "." + absJavaName);
                        contents = contents.replace("{1}", structName);
                        contents = contents.replace("{2}", absJavaName);
                        pkgMat.reset(contents);
                        pkgStr = "";
                        if (pkgMat.find()) {
                            pkgStr = pkgMat.group(1);
                        }
                        path = Tools.serPath + File.separator + pkgStr.replace(".", File.separator);
                        File executeJavaFolder = new File(path);
                        boolean createFlag = true;
                        if (executeJavaFolder.exists() && executeJavaFolder.isDirectory()) {
                            File executeJavaFile = new File(executeJavaFolder, structName + ".java");
                            createFlag = !executeJavaFile.exists();
                        } else {
                            executeJavaFolder.mkdirs();
                        }
                        if (createFlag) {
                            out = new OutputStreamWriter(new FileOutputStream(path + File.separator + structName + ".java"));
                            out.write(contents);
                            out.flush();
                            out.close();
                        }
                    }

                    if ((type & 0x3) != 0) {
                        msgHBuf.append("const int MSG_");
                        msgHBuf.append(structName);
                        msgHBuf.append("\t\t\t= ");
                        msgHBuf.append(cmdIndex);
                        msgHBuf.append(";\n");
                    }

                    if ((type & 0x1) != 0) {
                        msgCppBuf.append("\t\tCMD_DEAL(");
                        msgCppBuf.append(structName);
                        msgCppBuf.append(");\n ");
                        // rsp ����
                        String contents = OpenFile(Tools.rspFile);
                        Matcher pkgMat = pkgPat.matcher(contents);
                        String pkgStr = "";
                        if (pkgMat.find()) {
                            pkgStr = pkgMat.group(1);
                        }
                        contents = contents.replace("{0}", structName);
                        contents = contents.replace("{1}", String.format("%d", cmdIndex));
                        if (!temp) {

                            StringBuilder sb5 = new StringBuilder();
                            sb5.append("buffer.position(2);\n\t\tbuffer.putShort(code);");
                            contents = contents.replace("{5}", sb5.toString());
                        } else {
                            LogManager.getInstance().Log(">>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<");
                            contents = contents.replace("{5}", "");
                        }
                        StringBuilder sb1 = new StringBuilder();
                        StringBuilder sb2 = new StringBuilder();
                        StringBuilder sb3 = new StringBuilder();
                        int j = 0;
                        // funWriteBaseObj

                        for (int i = 0; i < fileds.size(); i += 3) {
                            sb1.append("@Getter @Setter protected " + Tools.FieldTypeReplace(fileds.get(i), fileds.get(i + 1)) + " " + fileds.get(i + 2) + ";\n\r\t");
                            if (fileds.get(i).equals("vector")) {
                                String name = replace(fileds.get(i + 1));
                                String name1 = Tools.FieldTypeReplace(name);
                                if (name1.equals(name)) {
                                    sb3.append(funWriteObj.replace("{0}", name1).replace("{1}", String.format("%d", ++j)));
                                } else {
                                    sb3.append(funWriteBase.replace("{0}", name1).replace("{1}", String.format("%d", ++j)));
                                }
                                sb2.append("WriteArray" + String.format("%d", j) + "(buffer," + fileds.get(i + 2) + ");\n\t\t");
                            } else {
                                String name = replace(fileds.get(i));//
                                String name1 = Tools.FieldTypeReplace(name);//
                                if (name1.equals(name))//
                                {//
                                    if (!bl)//
                                    {
                                        sb3.append(funWriteBaseObj);//
                                        LogManager.getInstance().Log("-------------------------" + bl + "       " + name);
                                        bl = true;//
                                    }
                                    sb2.append("WriteObj" + "(buffer," + fileds.get(i + 2) + ");\n\t\t");//
                                }//
                                else
                                    //
                                    sb2.append("Write" + Tools.FieldTypeReplace(fileds.get(i), fileds.get(i + 1)) + "(buffer," + fileds.get(i + 2) + ");\n\t\t");
                            }
                        }
                        bl = false;//
                        contents = contents.replace("{2}", sb1.toString());
                        contents = contents.replace("{3}", sb2.toString());
                        contents = contents.replace("{4}", sb3.toString());
                        // ���ͷ

                        String path = Tools.serPath + File.separator + pkgStr.replace(".", File.separator);
                        new File(path).mkdirs();
                        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(path + File.separator + structName + ".java"));
                        out.write(contents);
                        out.flush();
                        out.close();
                    }

                    hBuf.append("struct ");
                    hBuf.append(structName);
                    hBuf.append("\n{\n");

                    for (int i = 0; i < fileds.size(); i += 3) {
                        hBuf.append("\t");

                        hBuf.append((String) fileds.get(i));
                        if (fileds.get(i + 1) != null) {
                            hBuf.append("<");
                            hBuf.append((String) fileds.get(i + 1));
                            hBuf.append(">");
                        }

                        hBuf.append(" ");
                        hBuf.append((String) fileds.get(i + 1));

                        hBuf.append(";\n");
                    }

                    if ((type & 0x2) != 0) {
                        hBuf.append("\tvoid Send();\n");
                    }

                    hBuf.append("};\n");

                    hBuf.append("void Write");
                    hBuf.append(structName);
                    hBuf.append("(char*& buf,");
                    hBuf.append(structName);
                    hBuf.append("& value);\n");

                    cppBuf.append("void Write");
                    cppBuf.append(structName);
                    cppBuf.append("(char*& buf,");
                    cppBuf.append(structName);
                    cppBuf.append("& value)\n");
                    cppBuf.append("{\n");

                    for (int i = 0; i < fileds.size(); i += 3) {
                        cppBuf.append("\t");

                        if (fileds.get(i + 1) != null) {
                            cppBuf.append("WriteArray(buf,");

                            cppBuf.append((String) fileds.get(i + 1));
                            cppBuf.append(",");
                        } else {
                            cppBuf.append("Write");
                            cppBuf.append((String) fileds.get(i));
                            cppBuf.append("(buf,");
                        }

                        cppBuf.append("value.");
                        cppBuf.append((String) fileds.get(i + 1));

                        cppBuf.append(");\n");
                    }

                    cppBuf.append("}\n");

                    if ((type & 0x2) != 0) {
                        cppBuf.append("void ");
                        cppBuf.append(structName);
                        cppBuf.append("::Send()");
                        cppBuf.append("{\n");

                        cppBuf.append("\tBeginSend(");
                        cppBuf.append(structName);
                        cppBuf.append(");\n");

                        cppBuf.append("\tWrite");
                        cppBuf.append(structName);

                        cppBuf.append("(buf,*this);\n");

                        cppBuf.append("\tEndSend();\n");

                        cppBuf.append("}\n");
                    }

                    if ((type & 0x1) != 0) {
                        hBuf.append("void On");
                        hBuf.append(structName);
                        hBuf.append("(");
                        hBuf.append(structName);
                        hBuf.append("* value);\n");
                    }

                    hBuf.append("void Read");
                    hBuf.append(structName);
                    hBuf.append("(char*& buf,");
                    hBuf.append(structName);
                    hBuf.append("& value);\n");

                    cppBuf.append("void Read");
                    cppBuf.append(structName);
                    cppBuf.append("(char*& buf,");
                    cppBuf.append(structName);
                    cppBuf.append("& value)\n");
                    cppBuf.append("{\n");

                    for (int i = 0; i < fileds.size(); i += 3) {
                        cppBuf.append("\t");

                        if (fileds.get(i + 1) != null) {
                            cppBuf.append("ReadArray(buf,");

                            cppBuf.append((String) fileds.get(i + 1));
                            cppBuf.append(",");
                        } else {
                            cppBuf.append("Read");
                            cppBuf.append((String) fileds.get(i));
                            cppBuf.append("(buf,");
                        }

                        cppBuf.append("value.");
                        cppBuf.append((String) fileds.get(i + 1));

                        cppBuf.append(");\n");
                    }

                    cppBuf.append("}\n");

                    hBuf.append("\n");
            }
        }
        String contents = OpenFile(Tools.switchFile);
        Matcher pkgMat = pkgPat.matcher(contents);
        String pkgStr = "";
        if (pkgMat.find()) {
            pkgStr = pkgMat.group(1);
        }
        StringBuilder sb = new StringBuilder();
        String tmpSwitchStr = strSwitch.replace("\t", StringUtils.repeat(" ", 4));
        for (String str : classname) {
            sb.append(tmpSwitchStr.replace("{0}", str));
        }
        contents = contents.replace("{0}", sb.toString());
        String path = Tools.serPath + File.separator + pkgStr.replace(".", File.separator);
        new File(path).mkdirs();
        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(path + File.separator + "MessageHandler.java"));
        out.write(contents);
        out.flush();
        out.close();
    }

    private static String OpenFile(String file) throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream(file);
        InputStreamReader in = new InputStreamReader(new FileInputStream(file));
        Charset charset = Charset.forName("Utf-8");// 文件编码格式
        // �õ��ļ�ͨ��
        FileChannel fc = fis.getChannel();
        ByteBuffer buf = ByteBuffer.allocate((int) fc.size());
        fc.read(buf);
        // �ѻ����е�ǰλ�ûظ�Ϊ��
        buf.rewind();
        // ����������е�����
        String contents = charset.decode(buf).toString();
        return contents;
    }

    static void InitPer() {
        patCount = 0;

        // ͷ
        PatPush("^[ \t]*struct[ \t]+(\\w+)[ \t]*([<]?)([-]?)([>]?)[ \t]*", 0);
        // ע��
        PatPush("^[ \t]*//", 1);
        // �ֶ�
        PatPush("(\\w+)[ \t]*(<(\\w+)>)?[ \t]*(\\w+)", 2);
        // ����
        PatPush("^[ \t]*}", 3);
    }

    static void PatPush(String patStr, int type) {
        pats[patCount] = Pattern.compile(patStr);
        patType[patCount] = type;
        patCount += 1;
    }

    static String FieldTypeReplace(String name) {
        if (name.equals("string")) return "String";
        else if (name.equals("int8")) return "Byte";
        else if (name.equals("int16")) return "Short";
        else if (name.equals("int")) return "Integer";
        else if (name.equals("int64")) return "Long";
        else
            return name;
    }

    static String FieldTypeReplace(String name1, String name2) {
        String name;
        if (name1.equals("vector")) {
            name2 = replace(name2);
            name = FieldTypeReplace(name2) + "[]";
        } else
            name = FieldTypeReplace(name1);
        return name;
    }

    private static String replace(String name) {
        name = name.replace("<", "");
        return name.replace(">", "");
    }

    static String funReadBase = "protected {0}[] ReadArray{1}(ByteBuffer buf)" + "{" + "	int len = buf.getShort();" + "	{0}[] array =  new {0}[len];"
            + "	for(int i = 0; i< len; i++)" + "	{" + "		array[i] = Read{0}(buf);" + "	}" + " return array;" + "}";

    static String funReadObj = "protected {0}[] ReadArray{1}(ByteBuffer buf)" + "{" + "	int len = buf.getShort();" + "	{0}[] array =  new {0}[len];"
            + "	for(int i = 0; i< len; i++)" + "	{" + "		{0} req = new {0}();" + "		req.DeSerialize(buf);" + "		array[i] =  req;" + "	}" + " return array;" + "}";

    static String funWriteBase = "public void WriteArray{1}(ByteBuffer buf,{0}[] array)" + "{"
            + "  if(array==null){buf.putShort((short)0);return;}   buf.putShort((short)array.length);" + "	for(int i=0;i<array.length;i++)"
            + "	{ WriteByte(buf,(byte)1);Write{0}(buf, array[i]);}" + "}";

    static String funWriteObj = "public void WriteArray{1}(ByteBuffer buf,IRespose[] array)" + "{"
            + "  if(array==null){buf.putShort((short)0);return;}	buf.putShort((short)array.length);" + "	for(int i=0;i<array.length;i++)" + "	{"
            + "  if(array[i] == null)buf.put((byte)0); else buf.put((byte)1);" + "	if(array[i] != null){" + "		ByteBuffer newbuf = array[i].Serialize();"
            + "		for(int j=0;j<newbuf.limit();j++)" + "			buf.put(newbuf.get());" + "	}}" + "}";

    static String funWriteBaseObj = "public void WriteObj(ByteBuffer buf,IRespose resource)" + "{" + "	if(resource != null)" + "	{" + " buf.put((byte) 1); ByteBuffer newbuf = resource.Serialize();"
            + "		for(int j=0;j<newbuf.limit();j++)" + "			buf.put(newbuf.get());" + "	}else{ buf.put((byte) 0); }" + "}";
    static String strSwitch = "case Abstract{0}.msgcode: execute(session,buffer, new {0}());break;\n\t\t\t";
}